package com.jinyang.jetpackdemo.activity

import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.work.*
import com.jinyang.jetpackdemo.R
import com.jinyang.jetpackdemo.util.LjyLogUtil
import java.util.*
import java.util.concurrent.TimeUnit
import androidx.work.PeriodicWorkRequest

import androidx.work.WorkRequest
import androidx.work.OneTimeWorkRequest

import androidx.work.BackoffPolicy
import kotlinx.coroutines.delay
import androidx.work.WorkInfo

import androidx.work.WorkQuery
import com.google.common.util.concurrent.ListenableFuture


class WorkManagerActivity : AppCompatActivity() {
    @SuppressLint("IdleBatteryChargingConstraints")
    @RequiresApi(Build.VERSION_CODES.M)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_work_manager)
//        //1. 执行单次任务
        //创建workRequest
        val workRequest = OneTimeWorkRequestBuilder<MyWorker>().build()
        //使用enqueue()方法将其提交到WorkManager
        // WorkManager.getInstance(this).enqueue(workRequest)
        //2. 定期循环任务，如定期上传日志，定期缓存预加载的数据，定期备份等
        val workRequest2 =
            PeriodicWorkRequest.Builder(MyWorker::class.java, 3, TimeUnit.SECONDS).build()
//        WorkManager.getInstance(this).enqueue(workRequest2)
        //还可以定义具有灵活时间段的定期工作，如在每小时的最后 15 分钟内运行定期工作
        val workRequest22: WorkRequest = PeriodicWorkRequest.Builder(
            MyWorker::class.java,
            1, TimeUnit.HOURS,
            15, TimeUnit.MINUTES
        )
            .build()
        //3. 设置任务约束条件
        //如果不满足某个约束,WorkManager将停止工作，并且系统将在满足所有约束后重试工作
        val constraints = Constraints.Builder()
            //设备空闲状态时运行
            .setRequiresDeviceIdle(true)
            //特定的网络状态运行
            //NOT_REQUIRED	不需要网络
            //CONNECTED	任何可用网络
            //UNMETERED	需要不计量网络，如WiFi
            //NOT_ROAMING	需要非漫游网络
            //METERED	需要计量网络，如4G
            .setRequiredNetworkType(NetworkType.CONNECTED)
            //电量充足时运行
            .setRequiresBatteryNotLow(true)
            //充电时执行
            .setRequiresCharging(true)
            //存储空间足够时运行
            .setRequiresStorageNotLow(true)
            //指定是否在(Uri指定的)内容更新时执行本次任务
//            .addContentUriTrigger(Uri.EMPTY, true)
            .build()
        //4. 分配输入数据
        val inputData = Data.Builder().putString("name", "ljy").build()
        val workRequest3 = OneTimeWorkRequestBuilder<MyWorker>()
            .setConstraints(constraints)
            .setInputData(inputData)
            //5. 延时执行
            .setInitialDelay(1, TimeUnit.SECONDS)
            //6. 设置tag,以便取消工作或观察其进度,或者对任务进行分组
            //如果有一组在逻辑上相关的工作，对这些工作项进行标记可能也会很有帮助
            .addTag("myWorker")
            .build()
//        WorkManager.getInstance(this).enqueue(workRequest3)
        //7. 重试和退避政策
        //工作器返回 Result.retry()，系统将根据退避延迟时间和退避政策重新调度工作
        //退避延迟时间:指定了首次尝试后重试工作前的最短等待时间
        // 退避政策: 定义了在后续重试过程中，退避延迟时间随时间以怎样的方式增长,WorkManager 支持 2 个退避政策，即 LINEAR 和 EXPONENTIAL
        //每个工作请求都有退避政策和退避延迟时间。默认政策是 EXPONENTIAL，延迟时间为 10 秒，开发者可以在工作请求配置中替换此默认设置。
        val workRequest4: WorkRequest = OneTimeWorkRequest.Builder(MyWorker::class.java)
            .setBackoffCriteria(
                BackoffPolicy.LINEAR,
                OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
                TimeUnit.MILLISECONDS
            )
            .build()
        //8.创建任务链
        val request1 = OneTimeWorkRequest.Builder(MyWorker::class.java).build()
        val request2 = OneTimeWorkRequest.Builder(MyWorker::class.java).build()
        val request3 = OneTimeWorkRequest.Builder(MyWorker::class.java).build()
        val request4 = OneTimeWorkRequest.Builder(MyWorker::class.java).build()
        val request5 = OneTimeWorkRequest.Builder(MyWorker::class.java).build()
        val workConstraints = WorkManager.getInstance(this).beginWith(request1)
        workConstraints.then(request2).then(listOf(request3, request4)).enqueue()
        workConstraints.then(request5).enqueue()

        //唯一链:同一时间内队列里不能存在相同名称的任务
        //WorkManager.enqueueUniqueWork()：用于一次性工作
        //WorkManager.enqueueUniquePeriodicWork()：用于定期工作
        //应用场景：多次请求接口数据，如下单，更换头像等
        //例如替换头像要经历本地文件读取，压缩，上传三个任务，下面组成一个串行的任务连，并且设置唯一标识
        val requestLoadFromFile = OneTimeWorkRequest.Builder(MyWorker::class.java).build()
        val requestZip = OneTimeWorkRequest.Builder(MyWorker::class.java)
            .setInputData(createInputDataForUri()).build()
        val requestSubmitToService = OneTimeWorkRequest.Builder(MyWorker::class.java).build()
        WorkManager.getInstance(this).beginUniqueWork(
            "tagChangeImageHeader",
            ExistingWorkPolicy.REPLACE,
            requestLoadFromFile
        )
            .then(requestZip)
            .then(requestSubmitToService)
            .enqueue()
        //REPLACE：若相同，删除已有的任务，添加现有的任务。
        //KEEP：若相同，让已有的继续执行，不添加新任务，
        //APPEND：若相同，则添加新任务到已有任务链最末端。



        //9. Work状态
        //当WorkManager把任务加入队列后，会为每个WorkRequest对象提供一个LiveData;
        //LiveData持有WorkStatus,通过观察该 LiveData, 我们可以确定任务的当前状态, 并在任务完成后获取所有返回的值。
        //Work在其整个生命周期内经历了一系列 State 更改，状态的更改分为一次性任务的状态和周期性任务的状态。
        //    ENQUEUED,//已加入队列
        //    RUNNING,//运行中
        //    SUCCEEDED,//已成功
        //    FAILED,//已失败
        //    BLOCKED,//已挂起
        //    CANCELLED;//已取消
//        一次性任务状态：初始状态为 ENQUEUED，在满足其 Constraints 和初始延迟计时要求后立即运行，转为 RUNNING
        //根据工作的结果转为 SUCCEEDED、FAILED 状态；如果结果是Result.retry() ,它可能会回到 ENQUEUED 状态
        // SUCCEEDED、FAILED 和 CANCELLED 均表示此工作的终止状态,WorkInfo.State.isFinished() 都将返回 true
        //在此过程中，随时都可以取消工作，取消后工作将进入 CANCELLED 状态
        //定期任务状态:只有一个终止状态 CANCELLED
//        状态监听：


        //WorkManager 2.4.0 及更高版本还支持使用 WorkQuery 对象对已加入队列的作业进行复杂查询，
        // WorkQuery 支持按工作的标记、状态和唯一工作名称的组合进行查询
        val workQuery = WorkQuery.Builder
            .fromTags(listOf("syncTag"))
            .addStates(listOf(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
            .addUniqueWorkNames(
                listOf("preProcess", "sync")
            )
            .build()
        val workInfos: ListenableFuture<List<WorkInfo>> =
            WorkManager.getInstance(this).getWorkInfos(workQuery)

        //10. 更新和观察进度
        //Java: 使用Worker.setProgressAsync()更新进度
        //Kotlin：使用 CoroutineWorker.setProgress()更新进度
        //观察进度和上面的监听任务状态是一样的，使用 getWorkInfoBy…() 或 getWorkInfoBy…LiveData()
        val workRequest10 = OneTimeWorkRequestBuilder<MyWorker>()
            .build()
        WorkManager.getInstance(this).enqueue(workRequest10)
        WorkManager.getInstance(this)
            .getWorkInfoByIdLiveData(workRequest10.id)
            .observe(this, {
                if (it != null) {
                    LjyLogUtil.d("workRequest10:state=${it.state}")
                    val progress = it.progress;
                    val value = progress.getInt("progressValue", 0)
                    LjyLogUtil.d("workRequest10:progress=$value")
                }
            })


        //11.取消任务
        // WorkManager.getInstance(this).cancelAllWork()
        //取消一组带有相同标签的任务
//        WorkManager.getInstance(this).cancelAllWorkByTag("tagName")
//        WorkManager.getInstance(this).cancelUniqueWork("uniqueWorkName")
//        WorkManager.getInstance(this).cancelWorkById(UUID.randomUUID())
//正在运行的任务可能因为某些原因而停止运行，主要的原因有以下一些。
//
//明确要求取消它，可以调用WorkManager.cancelWorkById(UUID)方法。
//如果是唯一任务，将 ExistingWorkPolicy 为 REPLACE 的新 WorkRequest 加入到了队列中时，旧的 WorkRequest 会立即被视为已取消。
//添加的任务约束条件不再适合。
//系统出于某种原因指示应用停止工作。
//当任务停止后，WorkManager 会立即调用 ListenableWorker.onStopped()关闭可能保留的所有资源。

    }

    private fun createInputDataForUri(): Data {
        return Data.Builder().putString("name", "ljy").build()
    }
}

//2. 自定义Worker
class MyWorker(appContext: Context, workerParameters: WorkerParameters) :
    CoroutineWorker(appContext, workerParameters) {
    //    Worker(appContext, workerParameters) {

    //执行在一个单独的后台线程里
    override suspend fun doWork(): Result {
        val name = inputData.getString("name")
        LjyLogUtil.d("doWork start:name=$name")
        val p0 = workDataOf("progressValue" to 0)
        val p1 = workDataOf("progressValue" to 20)
        val p2 = workDataOf("progressValue" to 40)
        val p3 = workDataOf("progressValue" to 60)
        val p4 = workDataOf("progressValue" to 80)
        val p5 = workDataOf("progressValue" to 100)
        setProgress(p0)
        delay(1000)
        setProgress(p1)
        delay(1000)
        setProgress(p2)
        delay(1000)
        setProgress(p3)
        delay(1000)
        setProgress(p4)
        delay(1000)
        setProgress(p5)
        LjyLogUtil.d("doWork end")
        return Result.success()
        //- Result.success()：工作成功完成。
        // - Result.failure()：工作失败。
        // - Result.retry()：工作失败，根据其重试政策在其他时间尝试。
    }
}